package com.vpen.account.demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.vpen.account.demo.core.AccountDemoAccessDeniedHandler;
import com.vpen.account.demo.model.enums.AccountRoleEnum;
import com.vpen.clib.constant.CConstant;
import com.vpen.clib.result.CODE_MSG;
import com.vpen.clib.result.CResult;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

/**
 * spring security配置、
 *
 * @author 韦鹏
 * @see <a href="https://docs.spring.io/spring-security/reference/servlet/getting-started.html">spring security参考文档</a>
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ConfigurationProperties(prefix = "spring.security")
@Slf4j
public class AccountSecurityConfig {

    /**
     * 不需要授权验证的url
     */
    @Getter
    @Setter
    private String[] ignoreUrl;


    /**
     * url配置文件
     */
    @Resource
    private AccountDemo accountDemo;


    /**
     * 无权限处理器
     */
    @Resource
    private AccountDemoAccessDeniedHandler ystAccessDeniedHandler;

    /**
     * 登录成功处理器
     */
    @Resource
    private AuthenticationSuccessHandler loginSuccessHandler;

    /**
     * 登录是失败处理器
     */
    @Resource
    private AuthenticationFailureHandler loginFailureHandler;

    /**
     * 退出登录成功处理器
     */
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;

    @Resource
    private AuthenticationEntryPoint unLoginAuthenticationEntryPoint;

    /**
     * 单点登录
     */
    @Resource
    private SpringSessionBackedSessionRegistry redisSessionRegistry;

//    @Resource
//    private LoginPasswordDecryptFilter loginPasswordDecryptFilter;


    @Resource
    private ObjectMapper objectMapper;


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        String prefix = accountDemo.getWeb();
        return http
                .csrf().disable()
                .authorizeRequests()
                // 以/api/web/account/admin开头的所有连接，都必须是管理员才能访问的接口
                .antMatchers(accountDemo.getAdmin() + "/**").hasAuthority(AccountRoleEnum.ADMIN.getName())
                // 不需要认证就能访问资源
                .antMatchers(ignoreUrl).permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic()
                .and()
                .formLogin()
                .loginPage(prefix + "/login")
                .loginProcessingUrl(prefix + "/login")
                .defaultSuccessUrl(prefix + "/index")
                // 登录成功处理器
                .successHandler(loginSuccessHandler)
                // 登录失败处理器
                .failureHandler(loginFailureHandler)
                .and()
                .logout()
                .logoutUrl(prefix + "/logout")
                // 退出成功处理器
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .exceptionHandling()
                // 无访问权限处理器
                .accessDeniedHandler(ystAccessDeniedHandler)
                // 身份验证入口点
                .authenticationEntryPoint(unLoginAuthenticationEntryPoint)
                .and()
                // 在UsernamePasswordAuthenticationFilter过滤器前面添加密码解密的过滤器
//                .addFilterBefore(loginPasswordDecryptFilter, UsernamePasswordAuthenticationFilter.class)
                .sessionManagement()
                // 单个用户登录,如果已经有用户登录，那么前一个用户会回话会过期
                .maximumSessions(1)
                .maxSessionsPreventsLogin(false)
                .expiredUrl(prefix + "/login")
                // 用户session共享，用于单点登录
                .sessionRegistry(redisSessionRegistry)
                // 会话过期响应的事件的策略
                .expiredSessionStrategy(event -> {
                    HttpServletResponse response = event.getResponse();
                    response.setContentType(CConstant.CONTENT_TYPE);
                    CResult<Object> error = CResult.error(CODE_MSG.LOGIN);
                    error.setMsg("你的账号在别处登录,如果非本人操作,请及时修改密码.");
                    response.getWriter().print(objectMapper.writeValueAsString(error));
                    response.flushBuffer();
                }).and()
                .and()
                .csrf()
                .disable()
                .build();
    }


}